﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;

namespace CameraWrapper
{
    /// <summary>
    /// Wrapper Camera Driver for ACTi SDK10000. Create new Instance to start using
    /// </summary>
    public partial class SDK10000 : IDisposable
    {
        List<CameraInfo> cams;

        public SDK10000(CameraInfo cam1, CameraInfo cam2, CameraInfo cam3, CameraInfo cam4 = null)
        {
            cams = new List<CameraInfo>();
            if (cam1 != null) { cams.Add(cam1); if (cam1.Name == null) cam1.Name = "camera1"; if (cam1.SavePath == null) cam1.SavePath = @"C:\Recorder\camera1.avi"; }
            if (cam2 != null) { cams.Add(cam2); if (cam2.Name == null) cam2.Name = "camera2"; if (cam2.SavePath == null) cam2.SavePath = @"C:\Recorder\camera2.avi"; }
            if (cam3 != null) { cams.Add(cam3); if (cam3.Name == null) cam3.Name = "camera3"; if (cam3.SavePath == null) cam3.SavePath = @"C:\Recorder\camera3.avi"; }
            if (cam4 != null) { cams.Add(cam4); if (cam4.Name == null) cam4.Name = "camera4"; if (cam4.SavePath == null) cam4.SavePath = @"C:\Recorder\camera4.avi"; }
        }

        public SDK10000(CameraInfo[] cams)
        {
            this.cams = new List<CameraInfo>(); int i = 1;
            foreach (CameraInfo cam in cams)
            {
                if (cam != null)
                {
                    this.cams.Add(cam); 
                    if (cam.Name == null) cam.Name = "camera" + (i++).ToString(); 
                    if (cam.SavePath == null) cam.SavePath = string.Format(@"C:\Recorder\camera{1}.avi", i.ToString());
                }
            }
        }

        #region ONE CAMERA METHODs
        /// <summary>
        /// CONNECT TO ONE CAMERA.
        /// </summary>
        /// <param name="cam">The camera that you want to Connect to</param>
        /// <returns>TRUE if connect SUCCESS otherwise FALSE</returns>
        public bool Connect(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Create Handle
            try { cam.Handle = KOpenInterface(); if (cam.Handle == IntPtr.Zero) { MessageBox.Show("KOpenInterface FAIL !", cam.Name); return false; } }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KOpenInterface FAIL : " + cam.Name); return false; }

            // Test Audio & Set 100% Volume
            try { KSetMute(cam.Handle, true); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KSetMute true FAIL : " + cam.Name); return false; }
            try { SDK10000.KSetMute(cam.Handle, false); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KSetMute false FAIL : " + cam.Name); return false; }
            try { SDK10000.KSetVolume(cam.Handle, 100, 100); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KSetVolume FAIL"); return false; }

            // Connect to Video Server
            MEDIA_CONNECTION_CONFIG2 mcc = cam.MCC2;
            try { bool bMedia = KSetMediaConfig2(cam.Handle, ref mcc); if (!bMedia) { MessageBox.Show("KSetMediaConfig2 FAIL!", cam.Name); cam.Handle = IntPtr.Zero; return false; } }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KSetMediaConfig2 FAIL : " + cam.Name); return false; }

            try { bool bConnect = KConnect(cam.Handle); if (!bConnect) { MessageBox.Show("KConnect FAIL!", cam.Name); cam.Handle = IntPtr.Zero; return false; } }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KConnect FAIL : " + cam.Name); return false; }

            // Start Streaming
            try { bool bStream = KStartStreaming(cam.Handle); if (!bStream) { MessageBox.Show("KStartStreaming FAIL!", cam.Name); KDisconnect(cam.Handle); cam.Handle = IntPtr.Zero; return false; } }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KStartStreaming FAIL : " + cam.Name); return false; }

            // Set Media Render Info
            MEDIA_RENDER_INFO mri = cam.MRI;
            try { KSetRenderInfo(cam.Handle, ref mri); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KSetRenderInfo FAIL : " + cam.Name); return false; }

            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// DISCONNECT FROM A CAMERA.
        /// </summary>
        /// <param name="cam">The camera that you want to Disconnect from</param>
        /// <returns>TRUE if disconnect SUCCESS otherwise FALSE</returns>
        public bool Disconnect(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Disconnect Fail : Connect to SERVER First !", cam.Name); return false; }

            // Stop Playing Stream
            try { KStop(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Disconnect Fail at KStop : " + cam.Name); return false; }

            // Stop Streaming
            try { KStopStreaming(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Disconnect Fail at KStopStreaming : " + cam.Name); return false; }

            // Disconnect Server
            try { KDisconnect(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Disconnect Fail at KDisconnect : " + cam.Name); return false; }

            // Close Interface
            try { KCloseInterface(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Disconnect Fail at KCloseInterface : " + cam.Name); return false; }

            cam.Handle = IntPtr.Zero;
            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// PLAY PREVIEW ONE CAMERA (need connect to camera already)
        /// </summary>
        /// <param name="cam">The camera that you want to Play Preview</param>
        /// <param name="lowCPU">TRUE will decode I Frame only and help reduce CPU Usage</param>
        /// <returns>TRUE if Play SUCCESS otherwise FALSE</returns>
        public bool Play(CameraInfo cam, bool lowCPU = true)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Play Fail : Connect to SERVER First !", cam.Name); return false; }

            // Play video streaming
            try { KPlay(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Play Fail at KPlay : " + cam.Name); return false; }
            cam.IsPlaying = true;

            // Play video streaming
            try { if (lowCPU) KSetDecodeIFrameOnly(cam.Handle, true); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Play Fail at KSetDecodeIFrameOnly : " + cam.Name); return false; }

            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// PAUSE PREVIEW ONE CAMERA
        /// </summary>
        /// <param name="cam">The camera that you want to Pause Preview</param>
        /// <returns>TRUE if Pause SUCCESS otherwise FALSE</returns>
        public bool Pause(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Pause Fail : Connect to SERVER First !", cam.Name); return false; }

            // Pause video Playing
            try { KPause(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Play Fail at KPause : " + cam.Name); return false; }
            cam.IsPlaying = false;

            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// STOP PREVIEW ONE CAMERA
        /// </summary>
        /// <param name="cam">The camera that you want to Stop Preview</param>
        /// <returns>TRUE if Stop SUCCESS otherwise FALSE</returns>
        public bool Stop(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Stop Fail : Connect to SERVER First !", cam.Name); return false; }

            // Stop video Playing
            try { KStop(cam.Handle); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Stop Fail at KStop : " + cam.Name); return false; }
            cam.IsPlaying = false;

            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// RECORD TO FILE BY A CAMERA (need connect to camera already)
        /// </summary>
        /// <param name="cam">The camera that you want to Record to File</param>
        /// <returns>TRUE if Record SUCCESS otherwise FALSE</returns>
        public bool StartRecord(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Start Record Fail : Connect to SERVER First !", cam.Name); return false; }

            if (cam.IsRecording) { MessageBox.Show("Start Record Fail : Is Recording Already !", cam.Name); return false; }

            // Set Pre-record Time and Output File
            /*/try { KSetPrerecordTime(cam.Handle, 5); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "KSetPrerecordTime FAIL! : " + cam.Name); return false; } /*/

            // Set Writer Type
            try { KSetFileWriterType(cam.Handle, (int)FILE_WRITER_TYPES.FAVI); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Start Record Fail at KSetFileWriterType : " + cam.Name); return false; }

            // Check if folder exist
            if (!Directory.Exists(cam.SavePath)) Directory.CreateDirectory(Path.GetDirectoryName(cam.SavePath));

            // Check if file already exist
            //if (File.Exists(cam.SavePath)) File.Copy(cam.SavePath, cam.SavePath + ".bak"

            // Start recording
            try { cam.IsRecording = KStartRecord(cam.Handle, cam.SavePath); if (!cam.IsRecording) { MessageBox.Show("Start Record Fail at KStartRecord !", cam.Name); return false; } }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Start Record Fail at KStartRecord : " + cam.Name); return false; }

            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// STOP RECORD TO FILE FROM A CAMERA (need start record already)
        /// </summary>
        /// <param name="cam">The camera that you want to Stop Record to File</param>
        /// <returns>Return the File that you just finish Recording. NULL if Stop Record Fail</returns>
        public FileInfo StopRecord(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return null;

            if (!cam.IsRecording) { MessageBox.Show("Stop Record Fail : No Recording Detected !", cam.Name); return null; }

            // Stop recording
            MP4FILE_RECORD_INFO mrec = new MP4FILE_RECORD_INFO(); // Check Code HERE
            try { KStopRecord(cam.Handle, ref mrec); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Stop Record Fail at KStopRecord : " + cam.Name); return null; }
            cam.IsRecording = false; // Be careful DEAD LOCK because Start & Stop Record check for cam.IsRecording

            FileInfo fInfo = new FileInfo(cam.SavePath);
            if (!fInfo.Exists) return null;
            if (fInfo.Length == 0) return null;

            return fInfo; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// ON SOUND ONE CAMERA
        /// </summary>
        /// <param name="cam">The camera that you want to On Sound</param>
        /// <returns>TRUE if On SUCCESS otherwise FALSE</returns>
        public bool SoundON(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Sound ON Fail : Connect to SERVER First !", cam.Name); return false; }

            // Sound ON
            try { SDK10000.KSetMute(cam.Handle, false); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Sound ON Fail at KSetMute false : " + cam.Name); return false; }

            return true; // TO HERE mean SUCCESSFULLY
        }

        /// <summary>
        /// OFF SOUND ONE CAMERA
        /// </summary>
        /// <param name="cam">The camera that you want to Off Sound</param>
        /// <returns>TRUE if Off SUCCESS otherwise FALSE</returns>
        public bool SoundOFF(CameraInfo cam)
        {
            // Check if this Camera Initialized
            if (cam == null) return false;

            // Check if server connected
            if (cam.Handle == IntPtr.Zero) { MessageBox.Show("Sound OFF Fail : Connect to SERVER First !", cam.Name); return false; }

            // Sound OFF
            try { KSetMute(cam.Handle, true); }
            catch (Exception ex) { MessageBox.Show(ex.Message, "Sound OFF Fail at KSetMute true : " + cam.Name); return false; }

            return true; // TO HERE mean SUCCESSFULLY
        }
        #endregion

        #region ALL CAMERA METHODs
        /// <summary>
        /// CONNECT TO ALL CAMERA.
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Connect to</param>
        /// <returns>TRUE if connect SUCCESS otherwise FALSE</returns>
        public bool Connect(out List<CameraInfo> FailCameraList)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (Connect(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// CONNECT TO ALL CAMERA.
        /// </summary>
        /// <returns>TRUE if connect SUCCESS otherwise FALSE</returns>
        public bool Connect()
        {
            List<CameraInfo> cams;
            return Connect(out cams);
        }

        /// <summary>
        /// DISCONNECT FROM ALL CAMERA.
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Disconnect from</param>
        /// <returns>TRUE if connect SUCCESS otherwise FALSE</returns>
        public bool Disconnect(out List<CameraInfo> FailCameraList)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (Disconnect(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// DISCONNECT FROM ALL CAMERA.
        /// </summary>
        /// <returns>TRUE if connect SUCCESS otherwise FALSE</returns>
        public bool Disconnect()
        {
            List<CameraInfo> cams;
            return Disconnect(out cams);
        }

        /// <summary>
        /// PLAY PREVIEW ALL CAMERA (need connect to camera already)
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Play Preview</param>
        /// <param name="lowCPU">Set this true to reduce CPU Usage (Decode I Frame Only)</param>
        /// <returns>TRUE if Play SUCCESS otherwise FALSE</returns>
        public bool Play(out List<CameraInfo> FailCameraList, bool lowCPU = true)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (Play(cam, lowCPU) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// PLAY PREVIEW ALL CAMERA (need connect to camera already)
        /// </summary>
        /// <param name="lowCPU">Set this true to reduce CPU Usage (Decode I Frame Only)</param>
        /// <returns>TRUE if Play SUCCESS otherwise FALSE</returns>
        public bool Play(bool lowCPU = true)
        {
            List<CameraInfo> cams;
            return Play(out cams, lowCPU);
        }

        /// <summary>
        /// PAUSE PREVIEW ALL CAMERA
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Pause Preview</param>
        /// <returns>TRUE if Pause SUCCESS otherwise FALSE</returns>
        public bool Pause(out List<CameraInfo> FailCameraList)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (Pause(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// PAUSE PREVIEW ALL CAMERA
        /// </summary>
        /// <returns>TRUE if Pause SUCCESS otherwise FALSE</returns>
        public bool Pause()
        {
            List<CameraInfo> cams;
            return Pause(out cams);
        }

        /// <summary>
        /// STOP PREVIEW ALL CAMERA
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Stop Preview</param>
        /// <returns>TRUE if Stop SUCCESS otherwise FALSE</returns>
        public bool Stop(out List<CameraInfo> FailCameraList)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (Stop(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// STOP PREVIEW ALL CAMERA
        /// </summary>
        /// <returns>TRUE if Stop SUCCESS otherwise FALSE</returns>
        public bool Stop()
        {
            List<CameraInfo> cams;
            return Stop(out cams);
        }

        /// <summary>
        /// RECORD TO FILE BY ALL CAMERA (need connect to camera already)
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Record to File</param>
        /// <param name="NewFolder">Change Record Folder to new Location (Permenantly)</param>
        /// <param name="NewFile">Implement later : Change Record File Name (Permenantly). Now use the caminfo.SavePath instead</param>
        /// <returns>TRUE if Record SUCCESS otherwise FALSE</returns>
        public bool StartRecord(out List<CameraInfo> FailCameraList, string NewFolder = null, string NewFile = null)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (NewFolder != null) cam.SavePath = Path.Combine(NewFolder, Path.GetFileName(cam.SavePath));
                //if (NewFile != null) cam.SavePath = Path.Combine(Path.GetDirectoryName(cam.SavePath), NewFile);
                if (StartRecord(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// RECORD TO FILE BY ALL CAMERA (need connect to camera already)
        /// </summary>
        /// <param name="NewFolder">Change Record Folder to new Location (PERMENANTLY)</param>
        /// <param name="NewFile">Implement later : Change Record File Name (Permenantly). Now use the caminfo.SavePath instead</param>
        /// <returns>TRUE if Record SUCCESS otherwise FALSE</returns>
        public bool StartRecord(string NewFolder = null, string NewFile = null)
        {
            List<CameraInfo> cams;
            return StartRecord(out cams, NewFolder, NewFile);
        }

        /// <summary>
        /// STOP RECORD TO FILE FROM ALL CAMERA (need start record already)
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to Record to File</param>
        /// <returns>Return all Files that you success Recording</returns>
        public Dictionary<CameraInfo, FileInfo> StopRecord(out List<CameraInfo> FailCameraList)
        {
            Dictionary<CameraInfo, FileInfo> dict = new Dictionary<CameraInfo, FileInfo>();
            FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                FileInfo fInfo = StopRecord(cam);
                if (fInfo == null) FailCameraList.Add(cam);
                else dict.Add(cam, fInfo);
            }
            return dict;
        }
        /// <summary>
        /// STOP RECORD TO FILE FROM ALL CAMERA (need start record already)
        /// </summary>
        /// <returns>Return all Files that you success Recording</returns>
        public Dictionary<CameraInfo, FileInfo> StopRecord()
        {
            List<CameraInfo> cams;
            return StopRecord(out cams);
        }

        /// <summary>
        /// ON SOUND ALL CAMERA
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to ON Sound</param>
        /// <returns>TRUE if On SUCCESS otherwise FALSE</returns>
        public bool SoundON(out List<CameraInfo> FailCameraList)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (SoundON(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// ON SOUND ALL CAMERA
        /// </summary>
        /// <returns>TRUE if On SUCCESS otherwise FALSE</returns>
        public bool SoundON()
        {
            List<CameraInfo> cams;
            return SoundON(out cams);
        }

        /// <summary>
        /// OFF SOUND ALL CAMERA
        /// </summary>
        /// <param name="FailCameraList">List of Cameras that Fail to OFF sound</param>
        /// <returns>TRUE if Off SUCCESS otherwise FALSE</returns>
        public bool SoundOFF(out List<CameraInfo> FailCameraList)
        {
            bool b = true; FailCameraList = new List<CameraInfo>();
            foreach (CameraInfo cam in cams)
            {
                if (SoundOFF(cam) == false) { FailCameraList.Add(cam); b = false; }
            }
            return b;
        }
        /// <summary>
        /// OFF SOUND ALL CAMERA
        /// </summary>
        /// <returns>TRUE if Off SUCCESS otherwise FALSE</returns>
        public bool SoundOFF()
        {
            List<CameraInfo> cams;
            return SoundOFF(out cams);
        }
        #endregion

        #region ONE CAMERA PROPERTIES

        #endregion

        #region ALL CAMERA PROPERTIES
        /// <summary>
        /// CHECK IF ALL CAMERA IS RECORDING OR NOT
        /// </summary>
        public bool IsRecording
        {
            get
            {
                bool b = true;
                foreach (CameraInfo cam in cams)
                {
                    b = b && cam.IsRecording;
                }
                return b;
            }
        }

        /// <summary>
        /// CHECK IF ALL CAMERA IS PLAYING PREVIEW OR NOT
        /// </summary>
        public bool IsPlaying
        {
            get
            {
                bool b = true;
                foreach (CameraInfo cam in cams)
                {
                    b = b && cam.IsPlaying;
                }
                return b;
            }
        }
        #endregion

        /// <summary>
        /// For TESTING purpose only. Don't call this in RELEASE Version
        /// </summary>
        public void Test()
        {
            throw new NotImplementedException();
        }

        #region IDisposable Members
        /// <summary>
        /// Implement IDisposable Interface.
        /// </summary>
        public void Dispose() // Explicit (user call outside) call to this method (khac voi implicit call - Destructor tu dong goi)
        {
            Dispose(true); // Goi ham Dispose chinh
            GC.SuppressFinalize(this); // Tranh goi Finalizer (dich noi tu Destructor <- Call Implicit form Gabarge Collector)
        }

        /// <summary>
        /// Ham dispose chinh override IDisposable (se tu dong duoc goi neu cu phap "using"
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing) // Implicit call disposing false, Explicit call disposing true
        {
            if (disposing) // Explicit Call (nghia la co thang keu ham Dispose truc tiep)
            {
                #region Free other state (manage objects).

                #endregion
            }
            #region Free your own state (unmanage objects).
            foreach (CameraInfo cam in cams) { dispose(cam); }
#if false
            // Set large field to null.
            // FinalRemoveComObj ...v..v..

            if (g_book != null)
            {
                try { BookAppClose(g_book); }
                catch (Exception e) { }
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(g_sheet); // NEED TEST
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(g_book);
                g_book = null; g_sheet = null; g_bookPath = null;
            }
            GC.Collect(); // GC will collect this
            GC.WaitForPendingFinalizers(); // Wait for GC finish
#endif
            #endregion
        }

        /// <summary>
        /// dispose unmanage resource before destroy instance
        /// </summary>
        /// <param name="cam"></param>
        void dispose(CameraInfo cam)
        {
            // Check if init or not
            if (cam == null) return;

            // Check if OpenInterface or not
            if (cam.Handle == IntPtr.Zero) return;

            // Stop Recording
            StopRecord(cam);

            // Stop Streaming
            Stop(cam);

            // Disconnect from Server
            Disconnect(cam);
        }
        #endregion
    }
}